import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']  # 使用微软雅黑
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
data = {
    "2014-01-14": "title:An Operational Interpretation of Negative Probabilities and No-Signalling Models\nauthor:name\nusage:following your heart",
    "1985-08-01": "title:A review of extended probabilities\nauthor:name\nusage:following your heart",
    "2009-12-24": "title:Extended Probabilities Mathematical Foundations\nauthor:name\nusage:following your heart",
    "2005-07-14": "title:Half of a Coin: Negative Probabilities\nauthor:name\nusage:following your heart",
    "2010-08-06": "title:Interpretations of Negative Probabilitie\nauthor:name\nusage:following your heart",
    "2008-11-02": "title:Matrix Valued Brownian Motion and a Paper by Pólya\nauthor:name\nusage:following your heart",
    "2021-08-17": "title:Negative probabilities: what are they for?\nauthor:name\nusage:following your heart",
    "2018-07-26": "title:Negative probabilities: What they are and what they are for\nauthor:name\nusage:following your heart",
    "2024-05-02": "title:Negative Probability\nauthor:name\nusage:following your heart",
    "1984-09-06": "title:negative probability_Feynman\nauthor:name\nusage:following your heart",
    "2025-01-29": "title:Negative-Probability\nauthor:name\nusage:following your heart",
    "2019-05-08": "title:Paths, negative “probabilities”, and the Leggett-Garg inequalities\nauthor:name\nusage:following your heart",
    "2020-11-24": "title:We explain, on the example of Wigner's quasiprobability distribution, how negative probabilities may be used in the foundations of probability\nauthor:name\nusage:following your heart"
}

# 提取日期和事件
dates = list(data.keys())
events = list(data.values())

# 生成不重复的颜色
np.random.seed(42)
colors = plt.cm.get_cmap("tab10", len(dates)).colors

# 计算绘图年份
plot_years = [datetime.strptime(date, "%Y-%m-%d").year for date in dates]

# 计算文本框的 y 位置，确保无重叠
y_positions = {}
occupied_positions = []  # 记录已占用的 y 轴位置
min_y_spacing = 5  # 增加文本框之间的最小间隔
box_heights = {}  # 存储每个文本框的高度
box_widths = {}  # 存储每个文本框的宽度

# 最小文本框底部与主轴的间距
min_distance_from_axis = 2

for i, (date, event) in enumerate(zip(dates, events)):
    # 动态调整文本框宽度
    max_width = 80  # 默认最大宽度
    min_width = 25  # 设定最小宽度，防止过窄
    wrapped_text = event.split('\n')  # 使用 \n 换行

    # 计算文本框高度
    box_height = len(wrapped_text) * 0.6
    y_pos = (-1) ** i * (18 + box_height)  # 交替调整 y 位置

    # 确保文本框不会重叠
    while any(abs(y_pos - other_y) < min_y_spacing + box_height for other_y in occupied_positions):
        y_pos += min_y_spacing * (-1) ** i  # 交错调整 y 位置

    y_positions[date] = y_pos
    box_heights[date] = box_height
    occupied_positions.append(y_pos)

    # 缩小文本框宽度，避免箭头线遮挡
    for other_y in occupied_positions:
        if abs(y_pos - other_y) < min_y_spacing + box_height:  # 检测是否靠近箭头
            max_width = max(min_width, max_width - 20)  # 缩小文本框宽度
            wrapped_text = event.split('\n')  # 重新换行
            break

    box_widths[date] = max_width

# 计算日期文本的位置，确保不会重叠，并且紧贴主轴
date_positions = {}
year_offset = 0 # 设定年份与主轴的间距（可以调整这个值来增大或减小距离）
for i, date in enumerate(dates):
    x = datetime.strptime(date, "%Y-%m-%d").year
    y = 0.5  # 默认年份文本位置

    # **微调日期的位置，使其与圆点对齐，并调整与主轴的距离**
    date_positions[date] = x  # 将日期直接放置在主轴线上
    date_positions[date] += year_offset  # 增加偏移量来调整与主轴的距离

# 画图
fig, ax = plt.subplots(figsize=(24, 15))
ax.set_xlim(min(plot_years) - 5, max(plot_years) + 10)
ax.set_ylim(min(y_positions.values()) - 7, max(y_positions.values()) + 7)

# 画主时间轴（带箭头），设置箭头的 zorder 为 0，确保它在底层
ax.annotate("", xy=(max(plot_years) + 10, 0), xytext=(min(plot_years) - 5, 0),
            arrowprops=dict(arrowstyle="->", color="black", lw=3, shrinkA=0, shrinkB=10, zorder=0))


# 标注事件
for i, (date, event) in enumerate(zip(dates, events)):
    x, y = datetime.strptime(date, "%Y-%m-%d").year, y_positions[date]
    box_height = box_heights[date]
    box_width = box_widths[date]

    # 计算文本框顶部
    y_top = y + box_height / 2 + min_distance_from_axis
    arrow_start_y = 0
    y_bottom = y - box_height / 2 - min_distance_from_axis

    # 确保箭头的 zorder 较小
    if y > 0:
        # 如果文本框在上方，箭头指向顶部，年份文本放在下方
        ax.annotate("", xy=(x, y_bottom), xycoords='data',
                    xytext=(x, arrow_start_y), textcoords='data',
                    arrowprops=dict(arrowstyle="->", color=colors[i], lw=2, zorder=0))  # 所有箭头的 zorder 设置为 0
        ax.text(date_positions[date], -1, date, ha="center", va="top", fontsize=8, fontweight="bold", color="black", rotation=90, zorder=5)  # 文本框的 zorder 设置为 1
    else:
        # 如果文本框在下方，箭头指向底部，年份文本放在上方
        ax.annotate("", xy=(x, y_top), xycoords='data',
                    xytext=(x, arrow_start_y), textcoords='data',
                    arrowprops=dict(arrowstyle="->", color=colors[i], lw=2, zorder=0))  # 所有箭头的 zorder 设置为 0
        ax.text(date_positions[date], 1, date, ha="center", va="bottom", fontsize=8, fontweight="bold", color="black", rotation=90, zorder=5)  # 文本框的 zorder 设置为 1

    # 事件文本（直接使用 \n 换行）
    wrapped_text = event.replace('\n', '\n')  # 保持 \n 换行
    ax.text(x, y, wrapped_text, ha="center", va="center",
            fontsize=9, color="black", zorder=5,  # 所有事件文本的 zorder 设置为 1
            bbox=dict(facecolor="white", alpha=0.8, edgecolor=colors[i], boxstyle="round,pad=0.5"))

# 隐藏坐标轴
ax.set_yticks([])
ax.set_xticks([])
ax.spines["top"].set_visible(False)
ax.spines["left"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["bottom"].set_visible(False)

# 标题
ax.set_title("Time Line", fontsize=14, fontweight="bold")
# 保存图片到当前文件夹
plt.savefig("timeline.png", dpi=300, bbox_inches="tight")
# 显示图像
plt.show()